home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Tools 2
/
Amiga Tools 2.iso
/
tools
/
jade
/
src
/
lispcmds.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-03-09
|
56KB
|
2,456 lines
/* lispcmds.c -- Lots of standard Lisp functions
Copyright (C) 1993, 1994 John Harper <jsh@ukc.ac.uk>
This file is part of Jade.
Jade is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
Jade is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Jade; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#include "jade.h"
#include "jade_protos.h"
#include <string.h>
#ifdef NEED_MEMORY_H
# include <memory.h>
#endif
_PR void lispcmds_init(void);
_PR VALUE sym_load_path;
VALUE sym_load_path, sym_lisp_lib_dir;
/*
::doc:load_path::
A list of directory names. When `load' opens a lisp-file it searches each
directory named in this list in turn until the file is found or the list
is exhausted.
::end::
::doc:lisp_lib_dir::
The name of the directory in which the standard lisp files live.
::end::
*/
_PR VALUE cmd_quote(VALUE);
DEFUN("quote", cmd_quote, subr_quote, (VALUE args), V_SF, DOC_quote) /*
::doc:quote::
quote ARG
'ARG
Returns ARG.
::end:: */
{
if(CONSP(args))
return(VCAR(args));
return(NULL);
}
_PR VALUE cmd_function(VALUE);
DEFUN("function", cmd_function, subr_function, (VALUE args), V_SF, DOC_function) /*
::doc:function::
function ARG
#'ARG
Normally the same as `quote'. When being compiled, if ARG is not a symbol
it causes ARG to be compiled as a lambda expression.
::end:: */
{
if(CONSP(args))
return(VCAR(args));
return(NULL);
}
_PR VALUE cmd_defmacro(VALUE);
DEFUN("defmacro", cmd_defmacro, subr_defmacro, (VALUE args), V_SF, DOC_defmacro) /*
::doc:defmacro::
defmacro NAME LAMBDA-LIST [DOC-STRING] BODY...
Defines a macro called NAME with argument spec. LAMBDA-LIST, documentation
DOC-STRING (optional) and body BODY. The actual function value is
`(macro lambda LAMBDA-LIST [DOC-STRING] BODY...)'
Macros are called with their arguments un-evaluated, they are expected to
return a form which will be executed to provide the result of the expression.
A pathetic example could be,
(defmacro foo (x) (list 'cons nil x))
=> foo
(foo 'bar)
=> (nil . bar)
This makes `(foo X)' a pseudonym for `(cons nil X)'.
Note that macros are expanded at *compile-time* (unless, of course, the Lisp
code has not been compiled).
::end:: */
{
if(CONSP(args)
&& cmd_fset(VCAR(args),
cmd_cons(sym_macro, cmd_cons(sym_lambda, VCDR(args)))))
{
return(VCAR(args));
}
return(NULL);
}
_PR VALUE cmd_defun(VALUE);
DEFUN("defun", cmd_defun, subr_defun, (VALUE args), V_SF, DOC_defun) /*
::doc:defun::
defun NAME LAMBDA-LIST [DOC-STRING] BODY...
Defines a function called NAME with argument specification LAMBDA-LIST,
documentation DOC-STRING (optional) and body BODY. The actual function
value is,
`(lambda LAMBDA-LIST [DOC-STRING] BODY...)'
::end:: */
{
if(CONSP(args)
&& cmd_fset(VCAR(args), cmd_cons(sym_lambda, VCDR(args))))
{
return(VCAR(args));
}
return(NULL);
}
_PR VALUE cmd_defvar(VALUE);
DEFUN("defvar", cmd_defvar, subr_defvar, (VALUE args), V_SF, DOC_defvar) /*
::doc:defvar::
defvar NAME DEFAULT-VALUE [DOC-STRING]
Define a variable called NAME whose standard value is DEFAULT-
VALUE. If NAME is already bound to a value it is left as it is.
If the symbol NAME is marked buffer-local the *default value* of the
variable will be set (if necessary) not the local value.
::end:: */
{
if(CONSP(args) && CONSP(VCDR(args)))
{
GCVAL gcv_args;
VALUE sym = VCAR(args), val;
VALUE tmp = cmd_default_boundp(sym);
if(!tmp)
return(NULL);
PUSHGC(gcv_args, args);
val = cmd_eval(VCAR(VCDR(args)));
POPGC;
if(!val)
return(NULL);
if(NILP(tmp))
{
if(!cmd_set_default(sym, val))
return(NULL);
}
if(CONSP(VCDR(VCDR(args))))
{
if(!cmd_put(sym, sym_variable_documentation, VCAR(VCDR(VCDR(args)))))
return(NULL);
}
return(sym);
}
return(NULL);
}
_PR VALUE cmd_defconst(VALUE);
DEFUN("defconst", cmd_defconst, subr_defconst, (VALUE args), V_SF, DOC_defconst) /*
::doc:defconst::
defconst NAME VALUE [DOC-STRING]
Define a constant NAME whose (default) value is VALUE. If NAME is already
bound an error is signalled.
Constants are treated specially by the Lisp compiler, basically they are
hard-coded into the byte-code. For more details see the comments in
the compiler source (`lisp/compiler.jl').
::end:: */
{
if(CONSP(args))
{
VALUE tmp = cmd_default_boundp(VCAR(args));
if(tmp && !NILP(tmp))
{
return(cmd_signal(sym_error, list_2(MKSTR("Constant already bound"),
VCAR(args))));
}
tmp = cmd_defvar(args);
if(tmp)
return(cmd_set_const_variable(tmp, sym_nil));
return(tmp);
}
return(signal_arg_error(sym_nil, 1));
}
_PR VALUE cmd_car(VALUE);
DEFUN("car", cmd_car, subr_car, (VALUE cons), V_Subr1, DOC_car) /*
::doc:car::
car CONS-CELL
Returns the value stored in the car slot of CONS-CELL, or nil if CONS-CELL
is nil.
::end:: */
{
if(CONSP(cons))
return(VCAR(cons));
return(sym_nil);
}
_PR VALUE cmd_cdr(VALUE);
DEFUN("cdr", cmd_cdr, subr_cdr, (VALUE cons), V_Subr1, DOC_cdr) /*
::doc:cdr::
cdr CONS-CELL
Returns the value stored in the cdr slot of CONS-CELL, or nil if CONS-CELL
is nil.
::end:: */
{
if(CONSP(cons))
return(VCDR(cons));
return(sym_nil);
}
_PR VALUE cmd_list(VALUE);
DEFUN("list", cmd_list, subr_list, (VALUE args), V_SubrN, DOC_list) /*
::doc:list::
list ARGS...
Returns a new list with elements ARGS...
::end:: */
{
VALUE res = sym_nil;
VALUE *ptr = &res;
while(CONSP(args))
{
if(!(*ptr = cmd_cons(VCAR(args), sym_nil)))
return(NULL);
ptr = &VCDR(*ptr);
args = VCDR(args);
}
return(res);
}
_PR VALUE cmd_make_list(VALUE, VALUE);
DEFUN("make-list", cmd_make_list, subr_make_list, (VALUE len, VALUE init), V_Subr2, DOC_make_list) /*
::doc:make_list::
make-list LENGTH [INITIAL-VALUE]
Returns a new list with LENGTH members, each of which is initialised to
INITIAL-VALUE, or nil.
::end:: */
{
int i;
VALUE res = sym_nil;
VALUE *last;
DECLARE1(len, NUMBERP);
last = &res;
for(i = 0; i < VNUM(len); i++)
{
if(!(*last = cmd_cons(init, sym_nil)))
return(NULL);
last = &VCDR(*last);
}
return(res);
}
_PR VALUE cmd_append(VALUE);
DEFUN("append", cmd_append, subr_append, (VALUE args), V_SubrN, DOC_append) /*
::doc:append::
append LISTS...
Non-destructively concatenates each of it's argument LISTS... into one
new list which is returned.
::end:: */
{
VALUE res = sym_nil;
VALUE *resend = &res;
while(CONSP(args))
{
if(CONSP(VCAR(args)) && CONSP(VCDR(args)))
{
/* Only make a new copy if there's another list after this
one. */
*resend = copy_list(VCAR(args));
}
else
*resend = VCAR(args); /* Use the old object */
while(CONSP(*resend))
{
TEST_INT;
if(INT_P)
return(NULL);
resend = &(VCDR(*resend));
}
args = VCDR(args);
}
return(res);
}
_PR VALUE cmd_nconc(VALUE);
DEFUN("nconc", cmd_nconc, subr_nconc, (VALUE args), V_SubrN, DOC_nconc) /*
::doc:nconc::
nconc LISTS...
Destructively concatenates each of it's argument LISTS... into one new
list. Every LIST but the last is modified so that it's last cdr points
to the beginning of the next list. Returns the new list.
::end:: */
{
VALUE res = sym_nil;
VALUE *resend = &res;
while(CONSP(args))
{
VALUE tmp = VCAR(args);
if(CONSP(tmp))
{
*resend = tmp;
while(CONSP(VCDR(tmp)))
{
TEST_INT;
if(INT_P)
return(NULL);
tmp = VCDR(tmp);
}
resend = &VCDR(tmp);
}
args = VCDR(args);
}
return(res);
}
_PR VALUE cmd_rplaca(VALUE, VALUE);
DEFUN("rplaca", cmd_rplaca, subr_rplaca, (VALUE cons, VALUE car), V_Subr2, DOC_rplaca) /*
::doc:rplaca::
rplaca CONS-CELL NEW-CAR
Sets the value of the car slot in CONS-CELL to NEW-CAR. Returns the new
value.
::end:: */
{
DECLARE1(cons, CONSP);
VCAR(cons) = car;
return(car);
}
_PR VALUE cmd_rplacd(VALUE, VALUE);
DEFUN("rplacd", cmd_rplacd, subr_rplacd, (VALUE cons, VALUE cdr), V_Subr2, DOC_rplacd) /*
::doc:rplacd::
rplacd CONS-CELL NEW-CDR
Sets the value of the cdr slot in CONS-CELL to NEW-CAR. Returns the new
value.
::end:: */
{
DECLARE1(cons, CONSP);
VCDR(cons) = cdr;
return(cdr);
}
_PR VALUE cmd_reverse(VALUE);
DEFUN("reverse", cmd_reverse, subr_reverse, (VALUE head), V_Subr1, DOC_reverse) /*
::doc:reverse::
reverse LIST
Returns a new list which is a copy of LIST except that the members are in
reverse order.
::end:: */
{
VALUE res = sym_nil;
while(CONSP(head))
{
res = cmd_cons(VCAR(head), res);
if(res == NULL)
return(NULL);
head = VCDR(head);
TEST_INT;
if(INT_P)
return(NULL);
}
return(res);
}
_PR VALUE cmd_nreverse(VALUE);
DEFUN("nreverse", cmd_nreverse, subr_nreverse, (VALUE head), V_Subr1, DOC_nreverse) /*
::doc:nreverse::
nreverse LIST
Returns LIST altered so that it's members are in reverse order to what they
were. This function is destructive towards it's argument.
::end:: */
{
VALUE res = sym_nil;
VALUE nxt;
if(!CONSP(head))
return(sym_nil);
do {
if(CONSP(VCDR(head)))
nxt = VCDR(head);
else
nxt = NULL;
VCDR(head) = res;
res = head;
TEST_INT;
if(INT_P)
return(NULL);
} while((head = nxt));
return(res);
}
_PR VALUE cmd_assoc(VALUE, VALUE);
DEFUN("assoc", cmd_assoc, subr_assoc, (VALUE elt, VALUE list), V_Subr2, DOC_assoc) /*
::doc:assoc::
assoc ELT ASSOC-LIST
Searches ASSOC-LIST for a list whose first element is ELT. `assoc' uses
`equal' to compare elements. Returns the sub-list starting from the first
matching association.
For example,
(assoc 'three '((one . 1) (two . 2) (three . 3) (four . 4)))
=> (three . 3)
::end:: */
{
while(CONSP(list))
{
register VALUE car = VCAR(list);
if(CONSP(car) && (!value_cmp(elt, VCAR(car))))
return(car);
list = VCDR(list);
TEST_INT;
if(INT_P)
return(NULL);
}
return(sym_nil);
}
_PR VALUE cmd_assq(VALUE, VALUE);
DEFUN("assq", cmd_assq, subr_assq, (VALUE elt, VALUE list), V_Subr2, DOC_assq) /*
::doc:assq::
assq ELT ASSOC-LIST
Searches ASSOC-LIST for a list whose first element is ELT. `assq' uses `eq'
to compare elements. Returns the sub-list starting from the first matching
association.
::end:: */
{
while(CONSP(list))
{
register VALUE car = VCAR(list);
if(CONSP(car) && (elt == VCAR(car)))
return(car);
list = VCDR(list);
TEST_INT;
if(INT_P)
return(NULL);
}
return(sym_nil);
}
_PR VALUE cmd_rassoc(VALUE, VALUE);
DEFUN("rassoc", cmd_rassoc, subr_rassoc, (VALUE elt, VALUE list), V_Subr2, DOC_rassoc) /*
::doc:rassoc::
rassoc ELT ASSOC-LIST
Searches ASSOC-LIST for a cons-cell whose cdr element is `equal' to ELT.
Returns the first cons-cell which matches, or nil.
For example,
(rassoc 3 '((one . 1) (two . 2) (three . 3) (four . 4)))
=> (three . 3)
::end:: */
{
while(CONSP(list))
{
register VALUE car = VCAR(list);
if(CONSP(car) && (!value_cmp(elt, VCDR(car))))
return(car);
list = VCDR(list);
TEST_INT;
if(INT_P)
return(NULL);
}
return(sym_nil);
}
_PR VALUE cmd_rassq(VALUE, VALUE);
DEFUN("rassq", cmd_rassq, subr_rassq, (VALUE elt, VALUE list), V_Subr2, DOC_rassq) /*
::doc:rassq::
rassq ELT ASSOC-LIST
Searches ASSOC-LIST for a cons-cell whose cdr is `eq' to ELT.
Returns the first matching cons-cell, else nil.
::end:: */
{
while(CONSP(list))
{
register VALUE car = VCAR(list);
if(CONSP(car) && (elt == VCDR(car)))
return(car);
list = VCDR(list);
TEST_INT;
if(INT_P)
return(NULL);
}
return(sym_nil);
}
_PR VALUE cmd_nth(VALUE, VALUE);
DEFUN("nth", cmd_nth, subr_nth, (VALUE index, VALUE list), V_Subr2, DOC_nth) /*
::doc:nth::
nth INDEX LIST
Returns the INDEXth element of LIST. The first element has an INDEX of zero.
::end:: */
{
int i;
DECLARE1(index, NUMBERP);
i = VNUM(index);
while(i && CONSP(list))
{
list = VCDR(list);
i--;
}
if((!i) && CONSP(list))
return(VCAR(list));
return(sym_nil);
}
_PR VALUE cmd_nthcdr(VALUE index, VALUE list);
DEFUN("nthcdr", cmd_nthcdr, subr_nthcdr, (VALUE index, VALUE list), V_Subr2, DOC_nthcdr) /*
::doc:nthcdr::
nthcdr INDEX LIST
Returns the INDEXth cdr of LIST. The first is INDEX zero.
::end:: */
{
int i;
DECLARE1(index, NUMBERP);
i = VNUM(index);
while(i && CONSP(list))
{
list = VCDR(list);
i--;
}
if(!i)
return(list);
return(sym_nil);
}
_PR VALUE cmd_last(VALUE);
DEFUN("last", cmd_last, subr_last, (VALUE list), V_Subr1, DOC_last) /*
::doc:last::
last LIST
Returns the last element of LIST.
::end:: */
{
if(CONSP(list))
{
while(CONSP(VCDR(list)))
{
list = VCDR(list);
TEST_INT;
if(INT_P)
return(NULL);
}
return(list);
}
return(sym_nil);
}
_PR VALUE cmd_mapcar(VALUE, VALUE);
DEFUN("mapcar", cmd_mapcar, subr_mapcar, (VALUE fun, VALUE list), V_Subr2, DOC_mapcar) /*
::doc:mapcar::
mapcar FUNCTION LIST
Calls FUNCTION-NAME with each element of LIST as an argument in turn and
returns a new list constructed from the results, ie,
(mapcar (function (lambda (x) (1+ x))) '(1 2 3))
=> (2 3 4)
::end:: */
{
VALUE res = sym_nil;
VALUE *last = &res;
GCVAL gcv_list, gcv_argv, gcv_res;
VALUE argv = cmd_cons(fun, cmd_cons(sym_nil, sym_nil));
if(argv)
{
PUSHGC(gcv_res, res);
PUSHGC(gcv_argv, argv);
PUSHGC(gcv_list, list);
while(res && CONSP(list))
{
if(!(*last = cmd_cons(sym_nil, sym_nil)))
return(NULL);
VCAR(VCDR(argv)) = VCAR(list);
if(!(VCAR(*last) = cmd_funcall(argv)))
res = NULL;
else
{
last = &VCDR(*last);
list = VCDR(list);
}
TEST_INT;
if(INT_P)
{
res = NULL;
break;
}
}
POPGC; POPGC; POPGC;
}
return(res);
}
_PR VALUE cmd_mapc(VALUE, VALUE);
DEFUN("mapc", cmd_mapc, subr_mapc, (VALUE fun, VALUE list), V_Subr2, DOC_mapc) /*
::doc:mapc::
mapc FUNCTION LIST
Applies FUNCTION to each element in LIST, discards the results.
::end:: */
{
VALUE argv, res = list;
GCVAL gcv_argv, gcv_list;
if(!(argv = cmd_cons(fun, cmd_cons(sym_nil, sym_nil))))
return(NULL);
PUSHGC(gcv_argv, argv);
PUSHGC(gcv_list, list);
while(res && CONSP(list))
{
VCAR(VCDR(argv)) = VCAR(list);
if(!cmd_funcall(argv))
res = NULL;
list = VCDR(list);
TEST_INT;
if(INT_P)
res = NULL;
}
POPGC; POPGC;
return(res);
}
_PR VALUE cmd_member(VALUE, VALUE);
DEFUN("member", cmd_member, subr_member, (VALUE elt, VALUE list), V_Subr2, DOC_member) /*
::doc:member::
member ELT LIST
If ELT is a member of list LIST then return the tail of the list starting
from the matched ELT, ie,
(member 1 '(2 1 3))
=> (1 3)
`member' uses `equal' to compare atoms.
::end:: */
{
while(CONSP(list))
{
if(!value_cmp(elt, VCAR(list)))
return(list);
list = VCDR(list);
TEST_INT;
if(INT_P)
return(NULL);
}
return(sym_nil);
}
_PR VALUE cmd_memq(VALUE, VALUE);
DEFUN("memq", cmd_memq, subr_memq, (VALUE elt, VALUE list), V_Subr2, DOC_memq) /*
::doc:memq::
memq ELT LIST
If ELT is a member of list LIST then return the tail of the list starting
from the matched ELT, ie,
(memq 1 '(2 1 3))
=> (1 3)
`memq' uses `eq' to compare atoms.
::end:: */
{
while(CONSP(list))
{
if(elt == VCAR(list))
return(list);
list = VCDR(list);
TEST_INT;
if(INT_P)
return(NULL);
}
return(sym_nil);
}
_PR VALUE cmd_delete(VALUE, VALUE);
DEFUN("delete", cmd_delete, subr_delete, (VALUE elt, VALUE list), V_Subr2, DOC_delete) /*
::doc:delete::
delete ELT LIST
Returns LIST with any members `equal' to ELT destructively removed.
::end:: */
{
VALUE *head = &list;
while(CONSP(*head))
{
if(!value_cmp(elt, VCAR(*head)))
*head = VCDR(*head);
else
head = &VCDR(*head);
TEST_INT;
if(INT_P)
return(NULL);
}
return(list);
}
_PR VALUE cmd_delq(VALUE, VALUE);
DEFUN("delq", cmd_delq, subr_delq, (VALUE elt, VALUE list), V_Subr2, DOC_delq) /*
::doc:delq::
delq ELT LIST
Returns LIST with any members `eq' to ELT destructively removed.
::end:: */
{
VALUE *head = &list;
while(CONSP(*head))
{
if(elt == VCAR(*head))
*head = VCDR(*head);
else
head = &VCDR(*head);
TEST_INT;
if(INT_P)
return(NULL);
}
return(list);
}
_PR VALUE cmd_delete_if(VALUE, VALUE);
DEFUN("delete-if", cmd_delete_if, subr_delete_if, (VALUE pred, VALUE list), V_Subr2, DOC_delete_if) /*
::doc:delete_if::
delete-if FUNCTION LIST
Similar to `delete' except that a predicate function, FUNCTION-NAME, is
used to decide which elements to delete (remove destructively).
`delete-if' deletes an element if FUNCTION-NAME returns non-nil when
applied to that element, ie,
(delete-if '(lambda (x) (= x 1)) '(1 2 3 4 1 2))
=> (2 3 4 2)
::end:: */
{
VALUE *head = &list;
VALUE tmp;
while(CONSP(*head))
{
if(!(tmp = call_lisp1(pred, VCAR(*head))))
return(NULL);
if(!NILP(tmp))
*head = VCDR(*head);
else
head = &VCDR(*head);
TEST_INT;
if(INT_P)
return(NULL);
}
return(list);
}
_PR VALUE cmd_delete_if_not(VALUE, VALUE);
DEFUN("delete-if-not", cmd_delete_if_not, subr_delete_if_not, (VALUE pred, VALUE list), V_Subr2, DOC_delete_if_not) /*
::doc:delete_if_not::
delete-if-not FUNCTION LIST
Similar to `delete' except that a predicate function, FUNCTION-NAME, is
used to decide which elements to delete (remove destructively).
`delete-if-not' deletes an element if FUNCTION-NAME returns nil when
applied to that element, ie,
(delete-if-not '(lambda (x) (= x 1)) '(1 2 3 4 1 2))
=> (1 1)
::end:: */
{
VALUE *head = &list;
VALUE tmp;
while(CONSP(*head))
{
if(!(tmp = call_lisp1(pred, VCAR(*head))))
return(NULL);
if(NILP(tmp))
*head = VCDR(*head);
else
head = &VCDR(*head);
TEST_INT;
if(INT_P)
return(NULL);
}
return(list);
}
_PR VALUE cmd_vector(VALUE);
DEFUN("vector", cmd_vector, subr_vector, (VALUE args), V_SubrN, DOC_vector) /*
::doc:vector::
vector ARGS...
Returns a new vector with ARGS... as its elements.
::end:: */
{
VALUE res = make_vector(list_length(args));
if(res)
{
int i = 0;
while(CONSP(args))
{
VVECT(res)->vc_Array[i] = VCAR(args);
args = VCDR(args);
i++;
TEST_INT;
if(INT_P)
return(NULL);
}
}
return(res);
}
_PR VALUE cmd_make_vector(VALUE, VALUE);
DEFUN("make-vector", cmd_make_vector, subr_make_vector, (VALUE size, VALUE init), V_Subr2, DOC_make_vector) /*
::doc:make_vector::
make-vector SIZE [INITIAL-VALUE]
Creates a new vector of size SIZE. If INITIAL-VALUE is provided each element
will be set to that value, else they will all be nil.
::end:: */
{
int len;
VALUE res;
DECLARE1(size, NUMBERP);
len = VNUM(size);
res = make_vector(len);
if(res)
{
int i;
for(i = 0; i < len; i++)
VVECT(res)->vc_Array[i] = init;
}
return(res);
}
_PR VALUE cmd_arrayp(VALUE);
DEFUN("arrayp", cmd_arrayp, subr_arrayp, (VALUE arg), V_Subr1, DOC_arrayp) /*
::doc:arrayp::
arrayp ARG
Returns t when ARG is an array.
::end:: */
{
return((VECTORP(arg) || STRINGP(arg)) ? sym_t : sym_nil);
}
_PR VALUE cmd_aset(VALUE, VALUE, VALUE);
DEFUN("aset", cmd_aset, subr_aset, (VALUE array, VALUE index, VALUE new), V_Subr3, DOC_aset) /*
::doc:aset::
aset ARRAY INDEX NEW-VALUE
Sets element number INDEX (a positive integer) of ARRAY (can be a vector
or a string) to NEW-VALUE, returning NEW-VALUE. Note that strings
can only contain characters (ie, integers).
::end:: */
{
DECLARE2(index, NUMBERP);
switch(VTYPE(array))
{
case V_DynamicString:
if(VNUM(index) < STRING_LEN(array))
{
DECLARE3(new, NUMBERP);
VSTR(array)[VNUM(index)] = (u_char)VCHAR(new);
return(new);
}
break;
case V_Vector:
if(VNUM(index) < VVECT(array)->vc_Size)
{
VVECT(array)->vc_Array[VNUM(index)] = new;
return(new);
}
break;
default:
return(signal_arg_error(array, 1));
}
return(signal_arg_error(index, 2));
}
_PR VALUE cmd_aref(VALUE, VALUE);
DEFUN("aref", cmd_aref, subr_aref, (VALUE array, VALUE index), V_Subr2, DOC_aref) /*
::doc:aref::
aref ARRAY INDEX
Returns the INDEXth (a non-negative integer) element of ARRAY, which
can be a vector or a string. INDEX starts at zero.
::end:: */
{
DECLARE2(index, NUMBERP);
switch(VTYPE(array))
{
case V_StaticString:
case V_DynamicString:
if(VNUM(index) < STRING_LEN(array))
return(make_number(VSTR(array)[VNUM(index)]));
break;
case V_Vector:
if(VNUM(index) < VVECT(array)->vc_Size)
return(VVECT(array)->vc_Array[VNUM(index)]);
break;
default:
return(cmd_signal(sym_bad_arg, list_2(array, make_number(1))));
}
return(sym_nil);
}
_PR VALUE cmd_make_string(VALUE, VALUE);
DEFUN("make-string", cmd_make_string, subr_make_string, (VALUE len, VALUE init), V_Subr2, DOC_make_string) /*
::doc:make_string::
make-string LENGTH [INITIAL-VALUE]
Returns a new string of length LENGTH, each character is initialised to
INITIAL-VALUE, or to space if INITIAL-VALUE is not given.
::end:: */
{
VALUE res;
DECLARE1(len, NUMBERP);
res = make_string(VNUM(len) + 1);
if(res)
{
memset(VSTR(res), NUMBERP(init) ? (u_char)VCHAR(init) : ' ', VNUM(len));
VSTR(res)[VNUM(len)] = 0;
}
return(res);
}
static INLINE int
extend_concat(u_char **buf, int *bufLen, int i, int addLen)
{
u_char *newbuf;
int newbuflen;
if((i + addLen) < *bufLen)
return(TRUE);
newbuflen = (i + addLen) * 2;
newbuf = str_alloc(newbuflen);
if(newbuf)
{
memcpy(newbuf, *buf, i);
str_free(*buf);
*buf = newbuf;
*bufLen = newbuflen;
return(TRUE);
}
return(FALSE);
}
_PR VALUE cmd_concat(VALUE);
DEFUN("concat", cmd_concat, subr_concat, (VALUE args), V_SubrN, DOC_concat) /*
::doc:concat::
concat ARGS...
Concatenates all ARGS... into a single string, each argument can be a string,
a character or a list or vector of characters.
::end:: */
{
int buflen = 128;
u_char *buf = str_alloc(buflen);
if(buf)
{
VALUE res = NULL;
int i = 0;
while(CONSP(args))
{
VALUE arg = VCAR(args);
switch(VTYPE(arg))
{
int slen, j;
case V_StaticString:
case V_DynamicString:
slen = STRING_LEN(arg);
if(!extend_concat(&buf, &buflen, i, slen))
goto error;
memcpy(buf + i, VSTR(arg), slen);
i += slen;
break;
case V_Char:
if(!extend_concat(&buf, &buflen, i, 1))
goto error;
buf[i++] = VCHAR(arg);
break;
case V_Symbol:
if(arg != sym_nil)
break;
/* FALL THROUGH */
case V_Cons:
while(CONSP(arg))
{
VALUE ch = VCAR(arg);
if(VTYPEP(ch, V_Char))
{
if(!extend_concat(&buf, &buflen, i, 1))
goto error;
buf[i++] = VCHAR(ch);
}
arg = VCDR(arg);
TEST_INT;
if(INT_P)
goto error;
}
break;
case V_Vector:
for(j = 0; j < VVECT(arg)->vc_Size; j++)
{
if(VTYPEP(VVECT(arg)->vc_Array[j], V_Char))
{
if(!extend_concat(&buf, &buflen, i, 1))
goto error;
buf[i++] = VCHAR(VVECT(arg)->vc_Array[j]);
}
}
break;
}
args = VCDR(args);
}
res = string_dupn(buf, i);
if(res)
error:
str_free(buf);
return(res);
}
return(NULL);
}
_PR VALUE cmd_length(VALUE);
DEFUN("length", cmd_length, subr_length, (VALUE sequence), V_Subr1, DOC_length) /*
::doc:length::
length SEQUENCE
Returns the number of elements in SEQUENCE (a string, list or vector).
::end:: */
{
switch(VTYPE(sequence))
{
int i;
case V_StaticString:
case V_DynamicString:
return(make_number(STRING_LEN(sequence)));
break;
case V_Vector:
return(make_number(VVECT(sequence)->vc_Size));
break;
case V_Cons:
i = 0;
while(CONSP(sequence))
{
sequence = VCDR(sequence);
i++;
TEST_INT;
if(INT_P)
return(NULL);
}
return(make_number(i));
break;
case V_Symbol:
if(sequence == sym_nil)
return(make_number(0));
/* FALL THROUGH */
default:
cmd_signal(sym_bad_arg, list_2(sequence, make_number(1)));
return(NULL);
}
}
_PR VALUE cmd_copy_sequence(VALUE);
DEFUN("copy-sequence", cmd_copy_sequence, subr_copy_sequence, (VALUE seq), V_Subr1, DOC_copy_sequence) /*
::doc:copy_sequence::
copy-sequence SEQUENCE
Returns a new sequence whose elements are eq to those in SEQUENCE.
::end:: */
{
VALUE res = sym_nil;
switch(VTYPE(seq))
{
case V_Symbol:
if(!NILP(seq))
res = signal_arg_error(seq, 1);
break;
case V_Cons:
{
VALUE *last = &res;
while(CONSP(seq))
{
TEST_INT;
if(INT_P)
return(NULL);
if(!(*last = cmd_cons(VCAR(seq), sym_nil)))
return(NULL);
last = &VCDR(*last);
seq = VCDR(seq);
}
}
break;
case V_Vector:
res = make_vector(VVECT(seq)->vc_Size);
if(res)
{
int i;
for(i = 0; i < VVECT(seq)->vc_Size; i++)
VVECTI(res, i) = VVECTI(seq, i);
}
break;
case V_DynamicString:
case V_StaticString:
res = string_dupn(VSTR(seq), STRING_LEN(seq));
break;
default:
res = signal_arg_error(seq, 1);
}
return(res);
}
_PR VALUE cmd_elt(VALUE, VALUE);
DEFUN("elt", cmd_elt, subr_elt, (VALUE seq, VALUE index), V_Subr2, DOC_elt) /*
::doc:elt::
elt SEQUENCE INDEX
Return the element of SEQUENCE at position INDEX (counting from zero).
::end:: */
{
if(NILP(cmd_arrayp(seq)))
return(cmd_nth(index, seq));
else
return(cmd_aref(seq, index));
}
_PR VALUE cmd_prog1(VALUE);
DEFUN("prog1", cmd_prog1, subr_prog1, (VALUE args), V_SF, DOC_prog1) /*
::doc:prog1::
prog1 FORM1 FORMS...
First evals FORM1 then FORMS, returns the value that FORM1 gave.
::end:: */
{
if(CONSP(args))
{
VALUE res;
GCVAL gcv_args, gcv_res;
PUSHGC(gcv_args, args);
res = cmd_eval(VCAR(args));
if(res)
{
PUSHGC(gcv_res, res);
if(!cmd_progn(VCDR(args)))
res = NULL;
POPGC;
}
POPGC;
return(res);
}
return(NULL);
}
_PR VALUE cmd_prog2(VALUE);
DEFUN("prog2", cmd_prog2, subr_prog2, (VALUE args), V_SF, DOC_prog2) /*
::doc:prog2::
prog2 FORM1 FORM2 FORMS...
Evals FORM1 then FORM2 then the rest. Returns whatever FORM2 gave.
::end:: */
{
if(CONSP(args) && CONSP(VCDR(args)))
{
VALUE res;
GCVAL gcv_args, gcv_res;
PUSHGC(gcv_args, args);
if(cmd_eval(VCAR(args)))
{
res = cmd_eval(VCAR(VCDR(args)));
if(res)
{
PUSHGC(gcv_res, res);
if(!cmd_progn(VCDR(VCDR(args))))
res = NULL;
POPGC;
}
}
else
res = NULL;
POPGC;
return(res);
}
return(NULL);
}
_PR VALUE cmd_while(VALUE);
DEFUN("while", cmd_while, subr_while, (VALUE args), V_SF, DOC_while) /*
::doc:while::
while CONDITION FORMS...
Eval CONDITION, if it is non-nil then execute FORMS and repeat the
procedure, else return nil.
::end:: */
{
if(CONSP(args))
{
GCVAL gcv_args;
VALUE cond = VCAR(args), wval, body = VCDR(args);
PUSHGC(gcv_args, args);
while((wval = cmd_eval(cond)) && !NILP(wval))
{
TEST_INT;
if(INT_P || !cmd_progn(body))
{
wval = NULL;
break;
}
}
POPGC;
if(!wval)
return(NULL);
return(sym_nil);
}
return(NULL);
}
_PR VALUE cmd_if(VALUE);
DEFUN("if", cmd_if, subr_if, (VALUE args), V_SF, DOC_if) /*
::doc:if::
if CONDITION THEN-FORM [ELSE-FORMS...]
Eval CONDITION, if it is non-nil then eval THEN-FORM and return it's
result, else do an implicit progn with the ELSE-FORMS returning its value.
::end:: */
{
if(CONSP(args) && CONSP(VCDR(args)))
{
VALUE res;
GCVAL gcv_args;
PUSHGC(gcv_args, args);
res = cmd_eval(VCAR(args));
if(res)
{
if(!NILP(res))
res = cmd_eval(VCAR(VCDR(args)));
else
res = cmd_progn(VCDR(VCDR(args)));
}
POPGC;
return(res);
}
return(NULL);
}
_PR VALUE cmd_when(VALUE);
DEFUN("when", cmd_when, subr_when, (VALUE args), V_SF, DOC_when) /*
::doc:when::
when CONDITION FORMS...
Evaluates CONDITION, if it is non-nil evaluates FORMS.
::end:: */
{
VALUE res = sym_nil;
if(CONSP(args))
{
GCVAL gcv_args;
PUSHGC(gcv_args, args);
if((res = cmd_eval(VCAR(args))) && !NILP(res))
res = cmd_progn(VCDR(args));
POPGC;
}
return(res);
}
_PR VALUE cmd_unless(VALUE);
DEFUN("unless", cmd_unless, subr_unless, (VALUE args), V_SF, DOC_unless) /*
::doc:unless::
unless CONDITION FORMS...
Evaluates CONDITION, if it is nil evaluates FORMS.
::end:: */
{
VALUE res = sym_nil;
if(CONSP(args))
{
GCVAL gcv_args;
PUSHGC(gcv_args, args);
if((res = cmd_eval(VCAR(args))) && NILP(res))
res = cmd_progn(VCDR(args));
POPGC;
}
return(res);
}
_PR VALUE cmd_cond(VALUE);
DEFUN("cond", cmd_cond, subr_cond, (VALUE args), V_SF, DOC_cond) /*
::doc:cond::
cond (CONDITION FORMS... ) ...
Find the first CONDITION which has a value of t when eval'ed, then perform
a progn on its associated FORMS. If there are no FORMS with the CONDITION
then the value of the CONDITION is returned. If no CONDITION is t then
return nil.
An example,
(cond
((stringp foo)
(title "foo is a string"))
((numberp foo)
(setq bar foo)
(title "foo is a number"))
(t
(title "foo is something else...")))
Note the use of plain `t' on it's own for the last CONDITION, this is
like the last else in an else-if statement in C.
::end:: */
{
VALUE res = sym_nil;
GCVAL gcv_args;
PUSHGC(gcv_args, args);
while(CONSP(args) && CONSP(VCAR(args)))
{
VALUE cndlist = VCAR(args);
if(!(res = cmd_eval(VCAR(cndlist))))
break;
if(!NILP(res))
{
if(CONSP(VCDR(cndlist)))
{
if(!(res = cmd_progn(VCDR(cndlist))))
break;
}
break;
}
args = VCDR(args);
}
POPGC;
return(res);
}
_PR VALUE cmd_apply(VALUE);
DEFUN("apply", cmd_apply, subr_apply, (VALUE args), V_SubrN, DOC_apply) /*
::doc:apply::
apply FUNCTION ARGS... ARG-LIST
Calls FUNCTION passing all of ARGS to it as well as all elements in ARG-LIST.
ie,
(apply '+ 1 2 3 '(4 5 6))
=> 21
::end:: */
{
VALUE list = sym_nil, *last;
last = &list;
if(CONSP(args))
{
while(CONSP(VCDR(args)))
{
if(!(*last = cmd_cons(VCAR(args), sym_nil)))
return(NULL);
last = &VCDR(*last);
args = VCDR(args);
TEST_INT;
if(INT_P)
return(NULL);
}
if(!NILP(cmd_listp(VCAR(args))))
*last = VCAR(args);
else
return(cmd_signal(sym_bad_arg, LIST_1(VCAR(args))));
return(cmd_funcall(list));
}
return(cmd_signal(sym_missing_arg, LIST_1(make_number(1))));
}
_PR VALUE cmd_load(VALUE file, VALUE noerr_p, VALUE nopath_p, VALUE nosuf_p);
DEFUN_INT("load", cmd_load, subr_load, (VALUE file, VALUE noerr_p, VALUE nopath_p, VALUE nosuf_p), V_Subr4, DOC_load, "fLisp file to load:") /*
::doc:load::
load FILE [NO-ERROR-P] [NO-PATH-P] [NO-SUFFIX-P]
Attempt to open and then read-and-eval the file of Lisp code FILE.
For each directory named in the variable `load-path' tries the value of
FILE with `.jlc' (compiled-lisp) appended to it, then with `.jl' appended
to it, finally tries FILE without modification.
If NO-ERROR-P is non-nil no error is signalled if FILE can't be found.
If NO-PATH-P is non-nil the `load-path' variable is not used, just the value
of FILE.
If NO-SUFFIX-P is non-nil no suffixes are appended to FILE.
If the compiled version is older than it's source code, the source code is
loaded and a warning is displayed.
::end:: */
{
VALUE name = NULL, stream, path;
DECLARE1(file, STRINGP);
if(NILP(nopath_p))
{
path = cmd_symbol_value(sym_load_path, sym_nil);
if(!path)
return(NULL);
}
else
path = cmd_cons(MKSTR(""), sym_nil);
while(!name && CONSP(path))
{
u_char *dir = STRINGP(VCAR(path)) ? VSTR(VCAR(path)) : (u_char *)"";
if(NILP(nosuf_p))
{
bool jl_p = file_exists3(dir, VSTR(file), ".jl");
if(file_exists3(dir, VSTR(file), ".jlc"))
{
name = concat3(dir, VSTR(file), ".jlc");
if(jl_p)
{
VALUE tmp = concat3(dir, VSTR(file), ".jl");
if(file_mod_time(VSTR(tmp)) > file_mod_time(VSTR(name)))
{
messagef("Warning: %s newer than %s, using .jl",
VSTR(tmp), VSTR(name));
name = tmp;
}
}
}
else if(jl_p)
name = concat3(dir, VSTR(file), ".jl");
}
if(!name && file_exists2(dir, VSTR(file)))
name = concat2(dir, VSTR(file));
path = VCDR(path);
TEST_INT;
if(INT_P)
return(NULL);
}
if(!name)
{
if(NILP(noerr_p))
return(cmd_signal(sym_file_error,
list_2(MKSTR("Can't open lisp-file"), file)));
else
return(sym_nil);
}
if((stream = cmd_open(name, MKSTR("r"), sym_nil)) && FILEP(stream))
{
VALUE obj;
int c;
GCVAL gcv_stream;
PUSHGC(gcv_stream, stream);
c = stream_getc(stream);
while((c != EOF) && (obj = readl(stream, &c)))
{
TEST_INT;
if(INT_P || !cmd_eval(obj))
{
POPGC;
return(NULL);
}
}
POPGC;
return(sym_t);
}
return(NULL);
}
/*
* some arithmetic commands
*/
#define APPLY_OP( op ) \
if(CONSP(args) && NUMBERP(VCAR(args))) \
{ \
long sum = VNUM(VCAR(args)); \
args = VCDR(args); \
while(CONSP(args) && NUMBERP(VCAR(args))) \
{ \
sum = sum op VNUM(VCAR(args)); \
args = VCDR(args); \
TEST_INT; \
if(INT_P) \
return(NULL); \
} \
return(make_number(sum)); \
} \
return(NULL);
_PR VALUE cmd_plus(VALUE);
DEFUN("+", cmd_plus, subr_plus, (VALUE args), V_SubrN, DOC_plus) /*
::doc:plus::
+ NUMBERS...
Adds all NUMBERS together.
::end:: */
{
APPLY_OP( + )
}
_PR VALUE cmd_minus(VALUE);
DEFUN("-", cmd_minus, subr_minus, (VALUE args), V_SubrN, DOC_minus) /*
::doc:minus::
- NUMBER [NUMBERS...]
Either returns the negation of NUMBER or the value of NUMBER minus
NUMBERS
::end:: */
{
if(CONSP(args))
{
if(!CONSP(VCDR(args)))
return(make_number(-VNUM(VCAR(args))));
else
APPLY_OP( - )
}
return(NULL);
}
_PR VALUE cmd_product(VALUE);
DEFUN("*", cmd_product, subr_product, (VALUE args), V_SubrN, DOC_product) /*
::doc:product::
* NUMBERS...
Multiplies all NUMBERS together
::end:: */
{
APPLY_OP( * )
}
_PR VALUE cmd_divide(VALUE);
DEFUN("/", cmd_divide, subr_divide, (VALUE args), V_SubrN, DOC_divide) /*
::doc:divide::
/ NUMBERS...
Divides NUMBERS (in left-to-right order), ie,
(/ 100 2
=> 10
::end:: */
{
APPLY_OP( / )
}
_PR VALUE cmd_remainder(VALUE);
DEFUN("%", cmd_remainder, subr_remainder, (VALUE args), V_SubrN, DOC_remainder) /*
::doc:remainder::
% NUMBERS...
Applies the remainder operator between each of NUMBERS.
::end:: */
{
APPLY_OP( % )
}
_PR VALUE cmd_lognot(VALUE);
DEFUN("lognot", cmd_lognot, subr_lognot, (VALUE num), V_Subr1, DOC_lognot) /*
::doc:lognot::
lognot NUMBER
Returns the bitwise logical `not' of NUMBER.
::end:: */
{
DECLARE1(num, NUMBERP);
return(make_number(~VNUM(num)));
}
_PR VALUE cmd_not(VALUE);
DEFUN("not", cmd_not, subr_not, (VALUE arg), V_Subr1, DOC_not) /*
::doc:not::
not ARG
If ARG is nil returns t, else returns nil.
::end:: */
{
if(NILP(arg))
return(sym_t);
return(sym_nil);
}
_PR VALUE cmd_logior(VALUE);
DEFUN("logior", cmd_logior, subr_logior, (VALUE args), V_SubrN, DOC_logior) /*
::doc:logior::
logior NUMBERS...
Returns the bitwise logical `inclusive-or' of its arguments.
::end:: */
{
APPLY_OP( | )
}
_PR VALUE cmd_logxor(VALUE);
DEFUN("logxor", cmd_logxor, subr_logxor, (VALUE args), V_SubrN, DOC_logxor) /*
::doc:logxor::
logxor NUMBERS...
Returns the bitwise logical `exclusive-or' of its arguments.
::end:: */
{
APPLY_OP( ^ )
}
_PR VALUE cmd_or(VALUE);
DEFUN("or", cmd_or, subr_or, (VALUE args), V_SF, DOC_or) /*
::doc:or::
or FORMS...
Evals each FORM while they return nil, returns the first non-nil result or
nil if all FORMS return nil.
::end:: */
{
VALUE res = sym_nil;
GCVAL gcv_args, gcv_res;
PUSHGC(gcv_args, args);
PUSHGC(gcv_res, res);
while(res && CONSP(args) && NILP(res))
{
res = cmd_eval(VCAR(args));
args = VCDR(args);
TEST_INT;
if(INT_P)
res = NULL;
}
POPGC;
POPGC;
return(res);
}
_PR VALUE cmd_logand(VALUE);
DEFUN("logand", cmd_logand, subr_logand, (VALUE args), V_SubrN, DOC_logand) /*
::doc:logand::
logand NUMBERS...
Returns the bitwise logical `and' of its arguments.
::end:: */
{
APPLY_OP( & )
}
_PR VALUE cmd_and(VALUE);
DEFUN("and", cmd_and, subr_and, (VALUE args), V_SF, DOC_and) /*
::doc:and::
and FORMS...
Evals each FORM until one returns nil, it returns that value, or t if all
FORMS return t.
::end:: */
{
VALUE res = sym_t;
GCVAL gcv_args, gcv_res;
PUSHGC(gcv_args, args);
PUSHGC(gcv_res, res);
while(res && CONSP(args) && !NILP(res))
{
res = cmd_eval(VCAR(args));
args = VCDR(args);
TEST_INT;
if(INT_P)
res = NULL;
}
POPGC;
POPGC;
return(res);
}
_PR VALUE cmd_equal(VALUE, VALUE);
DEFUN("equal", cmd_equal, subr_equal, (VALUE val1, VALUE val2), V_Subr2, DOC_equal) /*
::doc:equal::
equal VALUE1 VALUE2
Compares VALUE1 and VALUE2, compares the actual structure of the objects not
just whether the objects are one and the same. ie, will return t for two
strings built from the same characters in the same order even if the strings'
location in memory is different.
::end:: */
{
if(value_cmp(val1, val2))
return(sym_nil);
return(sym_t);
}
_PR VALUE cmd_eq(VALUE, VALUE);
DEFUN("eq", cmd_eq, subr_eq, (VALUE val1, VALUE val2), V_Subr2, DOC_eq) /*
::doc:eq::
eq VALUE1 VALUE2
Returns t if VALUE1 and VALUE2 are one and the same object. Note that
this may or may not be true for numbers of the same value (see `eql').
::end:: */
{
if(val1 == val2)
return(sym_t);
return(sym_nil);
}
_PR VALUE cmd_eql(VALUE arg1, VALUE arg2);
DEFUN("eql", cmd_eql, subr_eql, (VALUE arg1, VALUE arg2), V_Subr2, DOC_eql) /*
::doc:eql::
eql ARG1 ARG2
Similar to `eq' except that numbers (integers, characters) with the same
value will always be considered `eql' (this may or may not be the case
with `eq'.
::end:: */
{
if(NUMBERP(arg1) && NUMBERP(arg2))
return(VNUM(arg1) == VNUM(arg2) ? sym_t : sym_nil);
else
return(arg1 == arg2 ? sym_t : sym_nil);
}
_PR VALUE cmd_string_head_eq(VALUE, VALUE);
DEFUN("string-head-eq", cmd_string_head_eq, subr_string_head_eq, (VALUE str1, VALUE str2), V_Subr2, DOC_string_head_eq) /*
::doc:string_head_eq::
string-head-eq STRING1 STRING2
Returns t if STRING2 matches the beginning of STRING1, ie,
(string-head-eq "foobar" "foo")
=> t
(string-head-eq "foo" "foobar")
=> nil
::end:: */
{
u_char *s1, *s2;
DECLARE1(str1, STRINGP);
DECLARE2(str2, STRINGP);
s1 = VSTR(str1);
s2 = VSTR(str2);
while(*s1 && *s2)
{
if(*s1++ != *s2++)
return(sym_nil);
}
if(*s1 || (*s1 == *s2))
return(sym_t);
return(sym_nil);
}
_PR VALUE cmd_num_eq(VALUE num1, VALUE num2);
DEFUN("=", cmd_num_eq, subr_num_eq, (VALUE num1, VALUE num2), V_Subr2, DOC_num_eq) /*
::doc:num_eq::
= NUMBER1 NUMBER2
Returns t if NUMBER1 and NUMBER2 are equal.
::end:: */
{
DECLARE1(num1, NUMBERP);
DECLARE2(num2, NUMBERP);
if(VNUM(num1) == VNUM(num2))
return(sym_t);
return(sym_nil);
}
_PR VALUE cmd_num_noteq(VALUE num1, VALUE num2);
DEFUN("/=", cmd_num_noteq, subr_num_noteq, (VALUE num1, VALUE num2), V_Subr2, DOC_num_noteq) /*
::doc:num_noteq::
/= NUMBER1 NUMBER2
Returns t if NUMBER1 and NUMBER2 are unequal.
::end:: */
{
DECLARE1(num1, NUMBERP);
DECLARE2(num2, NUMBERP);
if(VNUM(num1) != VNUM(num2))
return(sym_t);
return(sym_nil);
}
_PR VALUE cmd_gtthan(VALUE, VALUE);
DEFUN(">", cmd_gtthan, subr_gtthan, (VALUE arg1, VALUE arg2), V_Subr2, DOC_gtthan) /*
::doc:gtthan::
> ARG1 ARG2
Returns t if ARG1 is greater than ARG2. Note that this command isn't
limited to numbers, it can do strings, positions, marks, etc as well.
::end:: */
{
if(value_cmp(arg1, arg2) > 0)
return(sym_t);
return(sym_nil);
}
_PR VALUE cmd_gethan(VALUE, VALUE);
DEFUN(">=", cmd_gethan, subr_gethan, (VALUE arg1, VALUE arg2), V_Subr2, DOC_gethan) /*
::doc:gethan::
>= ARG1 ARG2
Returns t if ARG1 is greater-or-equal than ARG2. Note that this command
isn't limited to numbers, it can do strings, positions, marks, etc as well.
::end:: */
{
if(value_cmp(arg1, arg2) >= 0)
return(sym_t);
return(sym_nil);
}
_PR VALUE cmd_ltthan(VALUE, VALUE);
DEFUN("<", cmd_ltthan, subr_ltthan, (VALUE arg1, VALUE arg2), V_Subr2, DOC_ltthan) /*
::doc:ltthan::
< ARG1 ARG2
Returns t if ARG1 is less than ARG2. Note that this command isn't limited to
numbers, it can do strings, positions, marks, etc as well.
::end:: */
{
if(value_cmp(arg1, arg2) < 0)
return(sym_t);
return(sym_nil);
}
_PR VALUE cmd_lethan(VALUE, VALUE);
DEFUN("<=", cmd_lethan, subr_lethan, (VALUE arg1, VALUE arg2), V_Subr2, DOC_lethan) /*
::doc:lethan::
<= ARG1 ARG2
Returns t if ARG1 is less-or-equal than ARG2. Note that this command isn't
limited to numbers, it can do strings, positions, marks, etc as well.
::end:: */
{
if(value_cmp(arg1, arg2) <= 0)
return(sym_t);
return(sym_nil);
}
_PR VALUE cmd_plus1(VALUE);
DEFUN("1+", cmd_plus1, subr_plus1, (VALUE num), V_Subr1, DOC_plus1) /*
::doc:plus1::
1+ NUMBER
Return NUMBER plus 1.
::end:: */
{
DECLARE1(num, NUMBERP);
return(make_number(VNUM(num) + 1));
}
_PR VALUE cmd_sub1(VALUE);
DEFUN("1-", cmd_sub1, subr_sub1, (VALUE num), V_Subr1, DOC_sub1) /*
::doc:sub1::
1- NUMBER
Return NUMBER minus 1.
::end:: */
{
DECLARE1(num, NUMBERP);
return(make_number(VNUM(num) - 1));
}
_PR VALUE cmd_lsh(VALUE, VALUE);
DEFUN("lsh", cmd_lsh, subr_lsh, (VALUE num, VALUE shift), V_Subr2, DOC_lsh) /*
::doc:lsh::
lsh NUMBER COUNT
Shift the bits in NUMBER by COUNT bits to the left, a negative COUNT means
shift right.
::end:: */
{
DECLARE1(num, NUMBERP);
DECLARE2(shift, NUMBERP);
if(VNUM(shift) > 0)
return(make_number(VNUM(num) << VNUM(shift)));
/* ensure that a zero is in the top bit. */
return(make_number((VNUM(num) >> -VNUM(shift)) & 0x7fffffff));
}
_PR VALUE cmd_ash(VALUE, VALUE);
DEFUN("ash", cmd_ash, subr_ash, (VALUE num, VALUE shift), V_Subr2, DOC_ash) /*
::doc:ash::
ash NUMBER COUNT
Use an arithmetic shift to shift the bits in NUMBER by COUNT bits to the left,
a negative COUNT means shift right.
::end:: */
{
DECLARE1(num, NUMBERP);
DECLARE2(shift, NUMBERP);
if(VNUM(shift) > 0)
return(make_number(VNUM(num) << VNUM(shift)));
return(make_number(VNUM(num) >> -VNUM(shift)));
}
_PR VALUE cmd_zerop(VALUE);
DEFUN("zerop", cmd_zerop, subr_zerop, (VALUE num), V_Subr1, DOC_zerop) /*
::doc:zerop::
zerop NUMBER
t if NUMBER is zero.
::end:: */
{
if(NUMBERP(num) && (VNUM(num) == 0))
return(sym_t);
return(sym_nil);
}
_PR VALUE cmd_null(VALUE);
DEFUN("null", cmd_null, subr_null, (VALUE arg), V_Subr1, DOC_null) /*
::doc:null::
null ARG
Returns t if ARG is nil.
::end:: */
{
if(NILP(arg))
return(sym_t);
return(sym_nil);
}
_PR VALUE cmd_atom(VALUE);
DEFUN("atom", cmd_atom, subr_atom, (VALUE arg), V_Subr1, DOC_atom) /*
::doc:atom::
atom ARG
Returns t if ARG is not a cons-cell.
::end:: */
{
if(!CONSP(arg))
return(sym_t);
return(sym_nil);
}
_PR VALUE cmd_consp(VALUE);
DEFUN("consp", cmd_consp, subr_consp, (VALUE arg), V_Subr1, DOC_consp) /*
::doc:consp::
consp ARG
Returns t if ARG is a cons-cell.
::end:: */
{
if(CONSP(arg))
return(sym_t);
return(sym_nil);
}
_PR VALUE cmd_listp(VALUE);
DEFUN("listp", cmd_listp, subr_listp, (VALUE arg), V_Subr1, DOC_listp) /*
::doc:listp::
listp ARG
Returns t if ARG is a list, (either a cons-cell or nil).
::end:: */
{
if(NILP(arg) || CONSP(arg))
return(sym_t);
return(sym_nil);
}
_PR VALUE cmd_numberp(VALUE);
DEFUN("numberp", cmd_numberp, subr_numberp, (VALUE arg), V_Subr1, DOC_numberp) /*
::doc:numberp::
numberp ARG
Return t if ARG is a number.
::end:: */
{
if(NUMBERP(arg))
return(sym_t);
return(sym_nil);
}
_PR VALUE cmd_integerp(VALUE);
DEFUN("integerp", cmd_integerp, subr_integerp, (VALUE arg), V_Subr1, DOC_integerp) /*
::doc:integerp::
integerp ARG
Return t if ARG is a integer.
::end:: */
{
if(NUMBERP(arg))
return(sym_t);
return(sym_nil);
}
_PR VALUE cmd_stringp(VALUE);
DEFUN("stringp", cmd_stringp, subr_stringp, (VALUE arg), V_Subr1, DOC_stringp) /*
::doc:stringp::
stringp ARG
Returns t is ARG is a string.
::end:: */
{
if(STRINGP(arg))
return(sym_t);
return(sym_nil);
}
_PR VALUE cmd_vectorp(VALUE);
DEFUN("vectorp", cmd_vectorp, subr_vectorp, (VALUE arg), V_Subr1, DOC_vectorp) /*
::doc:vectorp::
vectorp ARG
Returns t if ARG is a vector.
::end:: */
{
if(VECTORP(arg))
return(sym_t);
return(sym_nil);
}
_PR VALUE cmd_functionp(VALUE);
DEFUN("functionp", cmd_functionp, subr_functionp, (VALUE arg), V_Subr1, DOC_functionp) /*
::doc:functionp::
functionp ARG
Returns t if ARG is a function (ie, a symbol or a list whose car is the
symbol `lambda'
::end:: */
{
if(SYMBOLP(arg))
{
if(!(arg = VSYM(arg)->sym_Function))
return(sym_nil);
}
switch(VTYPE(arg))
{
case V_Subr0:
case V_Subr1:
case V_Subr2:
case V_Subr3:
case V_Subr4:
case V_Subr5:
case V_SubrN:
return(sym_t);
case V_Cons:
arg = VCAR(arg);
if((arg == sym_lambda) || (arg == sym_autoload))
return(sym_t);
/* FALL THROUGH */
default:
return(sym_nil);
}
}
_PR VALUE cmd_special_form_p(VALUE);
DEFUN("special-form-p", cmd_special_form_p, subr_special_form_p, (VALUE arg), V_Subr1, DOC_special_form_p) /*
::doc:special_form_p::
special-form-p ARG
Returns t if ARG is a special-form.
::end:: */
{
if(SYMBOLP(arg))
{
if(!(arg = VSYM(arg)->sym_Function))
return(sym_nil);
}
if(VTYPEP(arg, V_SF))
return(sym_t);
return(sym_nil);
}
_PR VALUE cmd_subrp(VALUE arg);
DEFUN("subrp", cmd_subrp, subr_subrp, (VALUE arg), V_Subr1, DOC_subrp) /*
::doc:subrp::
subrp ARG
Returns t if arg is a primitive function.
::end:: */
{
switch(VTYPE(arg))
{
case V_Subr0:
case V_Subr1:
case V_Subr2:
case V_Subr3:
case V_Subr4:
case V_Subr5:
case V_SubrN:
case V_SF:
case V_Var:
return(sym_t);
default:
return(sym_nil);
}
}
_PR VALUE cmd_sequencep(VALUE arg);
DEFUN("sequencep", cmd_sequencep, subr_sequencep, (VALUE arg), V_Subr1, DOC_sequencep) /*
::doc:sequencep::
sequencep ARG
Returns t is ARG is a sequence (a list, vector or string).
::end:: */
{
if(NILP(arg) || CONSP(arg) || VECTORP(arg) || STRINGP(arg))
return(sym_t);
return(sym_nil);
}
_PR VALUE cmd_subr_documentation(VALUE subr, VALUE useVar);
DEFUN("subr-documentation", cmd_subr_documentation, subr_subr_documentation, (VALUE subr, VALUE useVar), V_Subr2, DOC_subr_documentation) /*
::doc:subr_documentation::
subr-documentation SUBR [USE-VAR]
Returns the doc-string associated with SUBR.
::end:: */
{
if(SYMBOLP(subr))
{
if(NILP(useVar))
{
if(VSYM(subr)->sym_Function)
subr = VSYM(subr)->sym_Function;
}
else
{
if(VSYM(subr)->sym_Value)
subr = VSYM(subr)->sym_Value;
}
}
switch(VTYPE(subr))
{
case V_Subr0:
case V_Subr1:
case V_Subr2:
case V_Subr3:
case V_Subr4:
case V_Subr5:
case V_SubrN:
case V_SF:
case V_Var:
return(cmd_read_file_from_to(MKSTR(DOC_FILE),
make_number(VSUBR(subr)->subr_DocIndex),
make_number((int)'\f')));
default:
return(sym_nil);
}
}
_PR VALUE cmd_subr_name(VALUE subr, VALUE useVar);
DEFUN("subr-name", cmd_subr_name, subr_subr_name, (VALUE subr, VALUE useVar), V_Subr2, DOC_subr_name) /*
::doc:subr_name::
subr-name SUBR [USE-VAR]
Returns the name (a string) associated with SUBR.
::end:: */
{
if(SYMBOLP(subr))
{
if(NILP(useVar))
{
if(VSYM(subr)->sym_Function)
subr = VSYM(subr)->sym_Function;
}
else
{
if(VSYM(subr)->sym_Value)
subr = VSYM(subr)->sym_Value;
}
}
switch(VTYPE(subr))
{
case V_Subr0:
case V_Subr1:
case V_Subr2:
case V_Subr3:
case V_Subr4:
case V_Subr5:
case V_SubrN:
case V_SF:
case V_Var:
return(VSUBR(subr)->subr_Name);
default:
return(sym_nil);
}
}
_PR VALUE cmd_eval_hook(VALUE);
DEFUN("eval-hook", cmd_eval_hook, subr_eval_hook, (VALUE args), V_SubrN, DOC_eval_hook) /*
::doc:eval_hook::
eval-hook HOOK ARGS...
Evaluate the hook, HOOK (a symbol), with arguments ARGS
The way hooks work is that the hook-symbol's value is a list of functions
to call. Each function in turn is called with ARGS until one returns non-nil,
this non-nil value is then the result of `eval-hook'. If all functions return
nil then `eval-hook' returns nil.
::end:: */
{
if(CONSP(args))
{
VALUE hook = VCAR(args);
VALUE alist = VCDR(args);
VALUE res = sym_nil;
GCVAL gcv_alist, gcv_hook;
PUSHGC(gcv_alist, alist);
switch(VTYPE(hook))
{
case V_StaticString:
case V_DynamicString:
if(!(hook = cmd_find_symbol(hook, sym_nil)))
goto end;
/* FALL THROUGH */
case V_Symbol:
hook = cmd_symbol_value(hook, sym_t);
if(VOIDP(hook))
goto end;
break;
}
PUSHGC(gcv_hook, hook);
while(res && NILP(res) && CONSP(hook))
{
res = funcall(VCAR(hook), alist);
hook = VCDR(hook);
TEST_INT;
if(INT_P)
res = NULL;
}
POPGC;
end:
POPGC;
return(res);
}
return(NULL);
}
_PR VALUE cmd_eval_hook2(VALUE hook, VALUE arg);
DEFUN("eval-hook2", cmd_eval_hook2, subr_eval_hook2, (VALUE hook, VALUE arg), V_Subr2, DOC_eval_hook2) /*
::doc:eval_hook2::
eval-hook2 HOOK ARG
Similar to `eval-hook', the only reason this function exists is because it
is easier to call a 2-argument function from C than an N-argument function.
::end:: */
{
VALUE res = sym_nil, alist;
/* Not possible to use GCVAL's since this is often called from C code
which may not be protected. */
int oldgci = gc_inhibit;
if(!(alist = cmd_cons(arg, sym_nil)))
return(NULL);
gc_inhibit = TRUE;
switch(VTYPE(hook))
{
case V_StaticString:
case V_DynamicString:
if(!(hook = cmd_find_symbol(hook, sym_nil)))
goto end;
/* FALL THROUGH */
case V_Symbol:
hook = cmd_symbol_value(hook, sym_t);
if(VOIDP(hook))
goto end;
break;
}
while(res && NILP(res) && CONSP(hook))
{
res = funcall(VCAR(hook), alist);
hook = VCDR(hook);
TEST_INT;
if(INT_P)
res = NULL;
}
end:
gc_inhibit = oldgci;
return(res);
}
_PR VALUE cmd_catch(VALUE);
DEFUN("catch", cmd_catch, subr_catch, (VALUE args), V_SF, DOC_catch) /*
::doc:catch::
catch TAG FORMS...
Evaluates FORMS, non-local exits are allowed with `(throw TAG)'.
The value of `catch' is either the value of the last FORM or the
value given to the throw command.
There are several pre-defined `catch'es which are,
'defun
Around all defuns, the `return' command uses this, it basically does
(throw 'defun X).
'exit
Exits one level of recursive-editing (but doesn't work in the top
level.
'top-level
At the top-level recursive-edit (ie, the one which you're in when
the editor is started).
'quit
Kills the editor.
::end:: */
/* Non-local exits don't bother with jmp_buf's and the like, they just
unwind normally through all levels of recursion with a NULL result.
This is slow but it's easy to work with. */
{
if(CONSP(args))
{
VALUE tag, res = NULL;
GCVAL gcv_args, gcv_tag;
PUSHGC(gcv_args, args);
tag = cmd_eval(VCAR(args));
if(tag)
{
PUSHGC(gcv_tag, tag);
if(!(res = cmd_progn(VCDR(args))))
{
if(throw_value && (VCAR(throw_value) == tag))
{
res = VCDR(throw_value);
throw_value = NULL;
}
}
POPGC;
}
POPGC;
return(res);
}
return(NULL);
}
_PR VALUE cmd_throw(VALUE, VALUE);
DEFUN("throw", cmd_throw, subr_throw, (VALUE tag, VALUE val), V_Subr2, DOC_throw) /*
::doc:throw::
throw TAG VALUE
Performs a non-local exit to the `catch' waiting for TAG and return
VALUE from it. TAG and VALUE are both evaluated fully.
::end:: */
{
/* Only one thing can use `throw_value' at once. */
if(!throw_value)
throw_value = cmd_cons(tag, val);
return(NULL);
}
_PR VALUE cmd_return(VALUE);
DEFUN("return", cmd_return, subr_return, (VALUE arg), V_Subr1, DOC_return) /*
::doc:return::
return [VALUE]
Arranges it so that the innermost defun returns VALUE (or nil) as its result.
::end:: */
{
if(!throw_value)
throw_value = cmd_cons(sym_defun, arg);
return(NULL);
}
_PR VALUE cmd_unwind_protect(VALUE);
DEFUN("unwind-protect", cmd_unwind_protect, subr_unwind_protect, (VALUE args), V_SF, DOC_unwind_protect) /*
::doc:unwind_protect::
unwind-protect BODY CLEANUP-FORMS...
Eval and return the value of BODY guaranteeing that the CLEANUP-FORMS will
be evalled no matter what happens (ie, error, non-local exit, etc) while
BODY is being evaluated.
::end:: */
{
if(CONSP(args))
{
VALUE res, throwval;
GCVAL gcv_args, gcv_res, gcv_throwval;
PUSHGC(gcv_args, args);
res = cmd_eval(VCAR(args));
PUSHGC(gcv_res, res);
throwval = throw_value;
throw_value = NULL;
PUSHGC(gcv_throwval, throwval);
if(!cmd_progn(VCDR(args)))
res = NULL;
throw_value = throwval;
POPGC; POPGC; POPGC;
return(res);
}
return(NULL);
}
void
lispcmds_init(void)
{
ADD_SUBR(subr_quote);
ADD_SUBR(subr_function);
ADD_SUBR(subr_defmacro);
ADD_SUBR(subr_defun);
ADD_SUBR(subr_defvar);
ADD_SUBR(subr_defconst);
ADD_SUBR(subr_car);
ADD_SUBR(subr_cdr);
ADD_SUBR(subr_list);
ADD_SUBR(subr_make_list);
ADD_SUBR(subr_append);
ADD_SUBR(subr_nconc);
ADD_SUBR(subr_rplaca);
ADD_SUBR(subr_rplacd);
ADD_SUBR(subr_reverse);
ADD_SUBR(subr_nreverse);
ADD_SUBR(subr_assoc);
ADD_SUBR(subr_assq);
ADD_SUBR(subr_rassoc);
ADD_SUBR(subr_rassq);
ADD_SUBR(subr_nth);
ADD_SUBR(subr_nthcdr);
ADD_SUBR(subr_last);
ADD_SUBR(subr_mapcar);
ADD_SUBR(subr_mapc);
ADD_SUBR(subr_member);
ADD_SUBR(subr_memq);
ADD_SUBR(subr_delete);
ADD_SUBR(subr_delq);
ADD_SUBR(subr_delete_if);
ADD_SUBR(subr_delete_if_not);
ADD_SUBR(subr_vector);
ADD_SUBR(subr_make_vector);
ADD_SUBR(subr_arrayp);
ADD_SUBR(subr_aset);
ADD_SUBR(subr_aref);
ADD_SUBR(subr_make_string);
ADD_SUBR(subr_concat);
ADD_SUBR(subr_length);
ADD_SUBR(subr_copy_sequence);
ADD_SUBR(subr_elt);
ADD_SUBR(subr_prog1);
ADD_SUBR(subr_prog2);
ADD_SUBR(subr_while);
ADD_SUBR(subr_if);
ADD_SUBR(subr_when);
ADD_SUBR(subr_unless);
ADD_SUBR(subr_cond);
ADD_SUBR(subr_apply);
ADD_SUBR(subr_load);
ADD_SUBR(subr_plus);
ADD_SUBR(subr_minus);
ADD_SUBR(subr_product);
ADD_SUBR(subr_divide);
ADD_SUBR(subr_remainder);
ADD_SUBR(subr_lognot);
ADD_SUBR(subr_not);
ADD_SUBR(subr_logior);
ADD_SUBR(subr_logxor);
ADD_SUBR(subr_or);
ADD_SUBR(subr_logand);
ADD_SUBR(subr_and);
ADD_SUBR(subr_equal);
ADD_SUBR(subr_eq);
ADD_SUBR(subr_eql);
ADD_SUBR(subr_string_head_eq);
ADD_SUBR(subr_num_eq);
ADD_SUBR(subr_num_noteq);
ADD_SUBR(subr_gtthan);
ADD_SUBR(subr_gethan);
ADD_SUBR(subr_ltthan);
ADD_SUBR(subr_lethan);
ADD_SUBR(subr_plus1);
ADD_SUBR(subr_sub1);
ADD_SUBR(subr_lsh);
ADD_SUBR(subr_ash);
ADD_SUBR(subr_zerop);
ADD_SUBR(subr_null);
ADD_SUBR(subr_atom);
ADD_SUBR(subr_consp);
ADD_SUBR(subr_listp);
ADD_SUBR(subr_numberp);
ADD_SUBR(subr_integerp);
ADD_SUBR(subr_stringp);
ADD_SUBR(subr_vectorp);
ADD_SUBR(subr_functionp);
ADD_SUBR(subr_special_form_p);
ADD_SUBR(subr_subrp);
ADD_SUBR(subr_subr_documentation);
ADD_SUBR(subr_sequencep);
ADD_SUBR(subr_subr_name);
ADD_SUBR(subr_eval_hook);
ADD_SUBR(subr_eval_hook2);
ADD_SUBR(subr_catch);
ADD_SUBR(subr_throw);
ADD_SUBR(subr_return);
ADD_SUBR(subr_unwind_protect);
INTERN(sym_load_path, "load-path");
VSYM(sym_load_path)->sym_Value = list_2(null_string, MKSTR(LISP_LIB_DIR));
DOC_VAR(sym_load_path, DOC_load_path);
INTERN(sym_lisp_lib_dir, "lisp-lib-dir");
VSYM(sym_lisp_lib_dir)->sym_Value = MKSTR(LISP_LIB_DIR);
DOC_VAR(sym_lisp_lib_dir, DOC_lisp_lib_dir);
}